package algoWing42.modelo;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;

/**
 */
public class Juego implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -1448627324920945321L;
	private static Juego UnicaInstancia;
	private AvionJugador jugador;
	private LinkedList<ObjetoDinamico> nuevosObjetosVivos; // a sacar
	private LinkedList<Avion> avionesActivos;
	private LinkedList<Disparo> disparosJugador;
	private LinkedList<Disparo> disparosEnemigos;
	private LinkedList<Item> itemsActivos;
	private LinkedList<Nivel> listaNiveles;
	private Nivel nivelCorriente;
	private int contadorTurnos;
	public static int ANCHO = 900;
	public static int LARGO = 677;
	private int nivelActual=0;
	
	// declaracion de 'numeros magicos'(variables globales estaticas):
	public static final int PUNTOS_NECESARIOS_PARA_PASAR_NIVEL = 1000;
	public static final int POSICION_INICIAL_JUGADOR_X = 450;
	public static final int POSICION_INICIAL_JUGADOR_Y = 500;

	public Juego() {
		avionesActivos = new LinkedList<Avion>();
		disparosJugador = new LinkedList<Disparo>();
		disparosEnemigos = new LinkedList<Disparo>();
		itemsActivos = new LinkedList<Item>();
		listaNiveles = new LinkedList<Nivel>();
		nuevosObjetosVivos = new LinkedList<ObjetoDinamico>();
		contadorTurnos = 0;

	}

	/**
	 * 'Basico' solo inicializa al jugador (de uso solo en tests)
	 */	

	public static Juego singleton() {
		if (UnicaInstancia == null) {
			UnicaInstancia = new Juego();
			UnicaInstancia.inicializarJuego(false);
		}

		return UnicaInstancia;
	}

	/****
	 * Evalua todas las colisiones posibles entre los objetos vivos. Para
	 * disminuir la complejidad se tuvo en mente colisiones premeditadas, por
	 * ejemplo: Jugador es el que se evalua versus el resto de los aviones no al
	 * revez. Sin embargo es facilmente extensible mediante la agregacion del
	 * dispatch correspondiente. 
	 */
	private void evaluarColisiones() {
		evaluarColisionesEntreJugadorYAviones();
		evaluarColisionesEntreJugadorEItems();
		evaluarColisionesEntreJugadorYDisparosEnemigos();
		evaluarColisionEntreDisparosJugadorYOtrosAviones();
	}

	private boolean juegoPasado() {
		if (listaNiveles.isEmpty()) {
			return true;
		}
		return false;
	}	

	/***
	 * Genera una accion (un 'turno' o unidad de tiempo) en todos los objetos.
	 * Hace vivir cada capa de objetos.
	 */
	public void juegoVivir() {
		contadorTurnos++;
		nivelCorriente.continuarCreandoNivel(contadorTurnos, this);

		vivirAviones();
		vivirDisparosEnemigos();
		vivirDisparosJugador();
		vivirItems();
		evaluarColisiones();
		limpiarMuertos();
		this.nivelPasado();
	}

	/**
	 * Limpia los objetos muertos de cada capa.
	 */
	private void limpiarMuertos() {
		limpiarAviones();
		limpiarDisparosEnemigos();
		limpiarDisparosJugador();
		limpiarItems();
	}

	private void nivelPasado() {
		if ((jugador.getScore() > PUNTOS_NECESARIOS_PARA_PASAR_NIVEL)) {
			if (!this.juegoPasado())
				this.nuevoNivel();
		} else {
			if (avionesActivos.size() < 3) {
				contadorTurnos = 0;
			}
		}
	}

	/**
	 * Inicializa el primer nivel en la lista y lo remueve. Contendria la logica
	 * de como armar un nivel basado en los niveles de la lista
	 */
	private void nuevoNivel() {
		this.nivelActual++;
		Nivel nivel = listaNiveles.removeFirst();
		contadorTurnos = 0;
		if (jugador == null) {
			jugador = new AvionJugador(POSICION_INICIAL_JUGADOR_X,
					POSICION_INICIAL_JUGADOR_Y);
			this.nuevosObjetosVivos.add(jugador);
		}
		jugador.score = 0;
		nivelCorriente = nivel;
		nivelCorriente.crearEnJuego(this);

	}

	/**
	 * Verifica que la posicion no este ocupada.
	 */
	public void verificarPosicion(Punto posicion) {
		if (this.jugador().getArea().getPosicion().esIgualA(posicion)) {
			throw new PosicionOcupada();
		}

		Iterator<Avion> iteradorAviones = avionesActivos.iterator();
		Iterator<Disparo> disparosJugador = this.disparosJugador.iterator();
		Iterator<Disparo> disparosEnemigos = this.disparosEnemigos.iterator();
		Iterator<Item> items = this.itemsActivos.iterator();

		while (iteradorAviones.hasNext()) {
			Avion avion = (Avion) iteradorAviones.next();

			if (avion.getArea().getPosicion().esIgualA(posicion)) {
				throw new PosicionOcupada();
			}
		}

		while (disparosJugador.hasNext()) {
			Disparo disparo = (Disparo) disparosJugador.next();

			if (disparo.getArea().getPosicion().esIgualA(posicion)) {
				throw new PosicionOcupada();
			}
		}

		while (disparosEnemigos.hasNext()) {
			Disparo disparo = (Disparo) disparosEnemigos.next();

			if (disparo.getArea().getPosicion().esIgualA(posicion)) {
				throw new PosicionOcupada();
			}
		}

		while (items.hasNext()) {
			Item item = (Item) items.next();

			if (item.getArea().getPosicion().esIgualA(posicion)) {
				throw new PosicionOcupada();
			}
		}
	}

	/** Getters & Setters **/
	public void agregarElJugador(AvionJugador unJugador) {
		jugador = unJugador;
		nuevosObjetosVivos.add(jugador);
	}

	public void agregarAvion(Avion unAvion) {
		avionesActivos.add(unAvion);
		nuevosObjetosVivos.add(unAvion);
	}

	public void agregarDisparoEnemigo(Disparo unDisparo) {
		disparosEnemigos.add(unDisparo);
		nuevosObjetosVivos.add(unDisparo);
	}

	public void agregarDisparoJugador(Disparo unDisparo) {
		disparosJugador.add(unDisparo);
		nuevosObjetosVivos.add(unDisparo);
	}

	public void agregarItem(Item unItem) {
		itemsActivos.add(unItem);
		nuevosObjetosVivos.add(unItem);
	}

	public AvionJugador jugador() {
		return jugador;
	}

	public LinkedList<Avion> avionesActivos() {
		return avionesActivos;
	}

	public LinkedList<Disparo> disparosJugador() {
		return disparosJugador;
	}

	public LinkedList<Disparo> disparosEnemigos() {
		return disparosEnemigos;
	}

	public LinkedList<Item> items() {
		return itemsActivos;
	}

	public LinkedList<ObjetoDinamico> getNuevosObjetosVivos() { // a sacar
		return this.nuevosObjetosVivos;
	}

	public void agregarNuevoObjetoVivo(ObjetoDinamico objeto) {
		this.nuevosObjetosVivos.add(objeto);
	}

	/** Singleton **/
	public static Juego singletonBasico() {
		if (UnicaInstancia == null) {
			UnicaInstancia = new Juego();
			UnicaInstancia.inicializarJuego(true);
		}

		return UnicaInstancia;
	}

	public static void finalizar() {
		UnicaInstancia = null;
	}

	public static boolean creado() {
		if (UnicaInstancia == null) {
			return false;
		} else {
			return true;
		}
	}

	public static void reemplazar(Juego nuevo) {
		Juego.UnicaInstancia = nuevo;
	}

	/** Metodos Privados **/
	
	private void inicializarJuego(final boolean basico) {
		listaNiveles.add(new Nivel1());
		listaNiveles.add(new Nivel2());// //agrego el mismo nivel para prueba
		listaNiveles.add(new Nivel1());
		listaNiveles.add(new Nivel2());
		// Agregar resto de niveles

		if (basico) {
			jugador = new AvionJugador(100, 100); // una posicion standard.

		} else {
			this.nuevoNivel();
		}
	}

	private void evaluarColisionesEntreJugadorYAviones() {
		Iterator<Avion> iteradorAviones = avionesActivos.iterator();
		while (iteradorAviones.hasNext()) {
			Avion avion = (Avion) iteradorAviones.next();
			jugador.evaluarColision(avion);
		}
	}

	private void evaluarColisionesEntreJugadorEItems() {
		Iterator<Item> iteradorItem = itemsActivos.iterator();
		while (iteradorItem.hasNext()) {
			Item item = (Item) iteradorItem.next();
			jugador.evaluarColision(item);
		}
	}

	private void evaluarColisionesEntreJugadorYDisparosEnemigos() {
		Iterator<Disparo> iteradorDisparosEnemigos = disparosEnemigos
				.iterator();
		while (iteradorDisparosEnemigos.hasNext()) {
			Disparo disparo = (Disparo) iteradorDisparosEnemigos.next();
			jugador.evaluarColision(disparo);
		}
	}

	@SuppressWarnings("unchecked")
	private void evaluarColisionEntreDisparosJugadorYOtrosAviones() {
		Collection<Disparo> listaClonadaDisparos = (Collection<Disparo>) disparosJugador
				.clone();
		Iterator<Disparo> iteradorDisparosJugador = listaClonadaDisparos
				.iterator();
		while (iteradorDisparosJugador.hasNext()) {
			Collection<Avion> listaClonadaAviones = (Collection<Avion>) avionesActivos
					.clone();
			Iterator<Avion> otroIteradorAviones = listaClonadaAviones
					.iterator();
			Disparo disparo = (Disparo) iteradorDisparosJugador.next();

			while (otroIteradorAviones.hasNext()) {
				Avion avion = (Avion) otroIteradorAviones.next();
				disparo.evaluarColision(avion);
			}
		}
	}

	private void vivirAviones() {
		Iterator<Avion> iteradorAviones = avionesActivos.iterator();
		while (iteradorAviones.hasNext()) {
			Avion avion = (Avion) iteradorAviones.next();
			avion.vivir();
		}
	}

	private void vivirDisparosEnemigos() {
		Iterator<Disparo> iteradorDisparosEnemigos = disparosEnemigos
				.iterator();
		while (iteradorDisparosEnemigos.hasNext()) {
			Disparo disparo = (Disparo) iteradorDisparosEnemigos.next();
			disparo.vivir();
		}
	}

	private void vivirDisparosJugador() {
		@SuppressWarnings("unchecked")
		LinkedList<Disparo> ListaDisparos = (LinkedList<Disparo>) disparosJugador
				.clone();
		Iterator<Disparo> iteradorDisparosJugador = ListaDisparos.iterator();
		while (iteradorDisparosJugador.hasNext()) {
			Disparo disparo = (Disparo) iteradorDisparosJugador.next();
			disparo.vivir();
		}
	}

	private void vivirItems() {
		Iterator<Item> iteradorItem = itemsActivos.iterator();

		while (iteradorItem.hasNext()) {
			Item item = (Item) iteradorItem.next();
			item.vivir();
		}
	}

	private void limpiarAviones() {
		Iterator<Avion> iteradorAviones = avionesActivos.iterator();
		while (iteradorAviones.hasNext()) {
			Avion avion = (Avion) iteradorAviones.next();

			if (avion.estaMuerto()) {
				iteradorAviones.remove();
			}
		}
	}

	private void limpiarDisparosEnemigos() {
		Iterator<Disparo> iteradorDisparosEnemigos = disparosEnemigos
				.iterator();
		while (iteradorDisparosEnemigos.hasNext()) {
			Disparo disparo = (Disparo) iteradorDisparosEnemigos.next();

			if (disparo.estaMuerto()) {
				iteradorDisparosEnemigos.remove();
			}
		}
	}

	private void limpiarDisparosJugador() {
		Iterator<Disparo> iteradorDisparosJugador = disparosJugador.iterator();
		while (iteradorDisparosJugador.hasNext()) {
			Disparo disparo = (Disparo) iteradorDisparosJugador.next();

			if (disparo.estaMuerto()) {
				iteradorDisparosJugador.remove();
			}
		}
	}

	private void limpiarItems() {
		Iterator<Item> iteradorItem = itemsActivos.iterator();
		while (iteradorItem.hasNext()) {
			Item item = (Item) iteradorItem.next();

			if (item.estaMuerto()) {
				iteradorItem.remove();
			}
		}
	}

	
	
	
	
	
	public void borrarNuevosObjetosVivos() {
		this.nuevosObjetosVivos = new LinkedList<ObjetoDinamico>();
	}
	
	public void matarNaves(){
		Iterator<Avion> naves=avionesActivos.iterator();
		while(naves.hasNext()){
			Avion nave= naves.next();
			if(nave.estaMuerto()==false){
			nave.destruir();}
		}
	}
	
	public int getNivelActual(){
		return this.nivelActual;
	}	
	
	/**
	 * GameOver
	 */
	public void juegoTerminado() {
		if (jugador.getEnergia() <= 0) {
			// continuar
		}
	}

}
